package com.joysuch.wwyt.patrol.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.BetweenFormatter;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.joysuch.wwyt.bp.bean.PositionCheckBean;
import com.joysuch.wwyt.bp.entity.BpDepart;
import com.joysuch.wwyt.bp.entity.BpDeviceFacilityCollection;
import com.joysuch.wwyt.bp.entity.BpMonitoringPointRel;
import com.joysuch.wwyt.bp.entity.BpProductionFacility;
import com.joysuch.wwyt.bp.entity.vo.AppCheckTaskVo;
import com.joysuch.wwyt.bp.repository.BpDepartDao;
import com.joysuch.wwyt.bp.repository.BpDeviceFacilityCollectionDao;
import com.joysuch.wwyt.bp.repository.BpMonitoringPointRelDao;
import com.joysuch.wwyt.bp.service.BpDeviceFacilityCollectionService;
import com.joysuch.wwyt.bp.service.BpInvestigateTaskService;
import com.joysuch.wwyt.bp.service.BpProductionFacilityService;
import com.joysuch.wwyt.common.constant.Constant;
import com.joysuch.wwyt.core.bean.ResultDto;
import com.joysuch.wwyt.core.bean.SimpleListBean;
import com.joysuch.wwyt.core.common.util.DateTimeUtil;
import com.joysuch.wwyt.core.common.util.JsonUtil;
import com.joysuch.wwyt.core.common.util.SimpleDateFormatCache;
import com.joysuch.wwyt.core.entity.BaseConfig;
import com.joysuch.wwyt.core.entity.BaseUser;
import com.joysuch.wwyt.core.enums.BaseConfigCodes;
import com.joysuch.wwyt.core.enums.Status;
import com.joysuch.wwyt.core.exception.ApiBusinessException;
import com.joysuch.wwyt.core.exception.ApiCoreException;
import com.joysuch.wwyt.core.repository.BaseUserDao;
import com.joysuch.wwyt.core.service.BaseConfigService;
import com.joysuch.wwyt.core.service.BaseRoleDataAuthService;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.mh.bean.MhPointValueAndSwitchStateBean;
import com.joysuch.wwyt.mh.entity.MhMonitoringPoint;
import com.joysuch.wwyt.mh.repository.MhMonitoringPointDao;
import com.joysuch.wwyt.mh.service.MhMonitoringRealTimeService;
import com.joysuch.wwyt.patrol.constant.CheckResultStatus;
import com.joysuch.wwyt.patrol.constant.ExceptProcessStatus;
import com.joysuch.wwyt.patrol.constant.TaskStatus;
import com.joysuch.wwyt.patrol.entity.*;
import com.joysuch.wwyt.patrol.entity.bean.*;
import com.joysuch.wwyt.patrol.entity.dto.*;
import com.joysuch.wwyt.patrol.entity.vo.CheckItemVo;
import com.joysuch.wwyt.patrol.entity.vo.CheckPointDetailVo;
import com.joysuch.wwyt.patrol.entity.vo.WebTaskCheckVo;
import com.joysuch.wwyt.patrol.entity.vo.WebTaskRecordPointVo;
import com.joysuch.wwyt.patrol.enums.CycleType;
import com.joysuch.wwyt.patrol.enums.CycleUnit;
import com.joysuch.wwyt.patrol.enums.DeviceTypeEnum;
import com.joysuch.wwyt.patrol.enums.RequestBy;
import com.joysuch.wwyt.patrol.mapper.PatrolCheckPlanMapper;
import com.joysuch.wwyt.patrol.mapper.PatrolTaskMapper;
import com.joysuch.wwyt.patrol.service.*;
import com.joysuch.wwyt.risk.entity.RiskDistrict;
import com.joysuch.wwyt.risk.entity.RiskPoint;
import com.joysuch.wwyt.risk.service.RiskPointService;
import com.joysuch.wwyt.util.DateUtils;
import com.joysuch.wwyt.util.FileUtil;
import com.joysuch.wwyt.util.RegionUtil;
import com.joysuch.wwyt.workflow.api.bean.TrajectoryLastLocationResp;
import com.joysuch.wwyt.workflow.api.service.PersonTrajectoryService;
import com.joysuch.wwyt.workflow.api.service.impl.PersonTrajectoryImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author heqifeng
 * @since 2022-02-28
 */
@Service
@Slf4j
public class PatrolTaskServiceImpl extends ServiceImpl<PatrolTaskMapper, PatrolTask> implements PatrolTaskService {
    @Autowired
    private PatrolTaskMapper taskMapper;
    @Autowired
    private PatrolCheckPlanService planService;
    @Autowired
    private PatrolCheckPlanMapper planMapper;
    @Autowired
    private PatrolTaskAssignService taskAssignService;
    @Autowired
    private PatrolUnfinishTaskService unfinishTaskService;
    @Autowired
    private PatrolTaskAssignService assignService;
    @Autowired
    private PatrolCheckItemService checkItemService;
    @Autowired
    private BpMonitoringPointRelDao relDao;
    @Autowired
    private MhMonitoringPointDao mhMonitoringPointDao;
    @Autowired
    private BpDepartDao departDao;
    @Autowired
    private BaseUserDao userDao;
    @Autowired
    private PatrolRecordService recordService;
    @Autowired
    private PatrolRecordItemService recordItemService;
    @Autowired
    private PatrolCheckPointService pointService;
    @Autowired
    private PersonTrajectoryImpl personTrajectory;
    @Autowired
    private PatrolSnapshotPathService snapshotPathService;
    @Autowired
    private BaseRoleDataAuthService authService;
    @Autowired
    private BpDeviceFacilityCollectionDao collectionDao;
    @Autowired
    private MhMonitoringRealTimeService mhMonitoringRealTimeService;
    @Autowired
    private PersonTrajectoryService personTrajectoryService;
    @Autowired
    private BaseUserDao baseUserDao;
    @Autowired
    private BaseConfigService baseConfigService;
    @Autowired
    private PatrolPlanPointMappingService patrolPlanPointMappingService;

    @Autowired
    private RiskPointService riskPointService;

    @Autowired
    private BpInvestigateTaskService bpInvestigateTaskService;

    @Autowired
    private BpProductionFacilityService bpProductionFacilityService;

    @Autowired
    private BpDeviceFacilityCollectionService bpDeviceFacilityCollectionService;


    // <editor-fold desc="任务生成逻辑">

    @Scheduled(cron = "0 2 0 * * ?")
    public synchronized void produce() {
        producePatrolTask(null);
    }

    @Override
    public void producePatrolTask(Long planId) {
        log.error("patrol schedule produce task start...");
        List<PatrolCheckPlan> plans = new ArrayList<>();
        final Date currDate = new Date();

        // 此三列表只做add操作。
        final List<PatrolTaskAssign> assigns = new ArrayList<>();
        final List<PatrolUnfinishTask> unFinishMappings = new ArrayList<>();
        final List<PatrolTask> tasks = new ArrayList<>();

        if (planId != null) {
            plans.add(planMapper.selectById(planId));
        } else {
            plans.addAll(planService.getEfficientPlans());
        }
        if (CollectionUtil.isEmpty(plans)) {
            return;
        }
        // 准备计划数据
        Map<Long,PatrolCheckPlan> planMap = plans.stream().collect(Collectors.toMap(PatrolCheckPlan::getId, plan -> plan));
        // 生成
        produceSegment(plans, currDate, assigns, unFinishMappings, tasks);

        if (CollectionUtil.isNotEmpty(tasks)) {
            batchSave(tasks, 1000);
            // saveBatch(tasks);
            for (int i = tasks.size() - 1; i >=0; i--) {
                PatrolTask each = tasks.get(i);
                List<PatrolTaskAssign> taskAssigns = taskAssignService.genAssignByPlanAndTask(planMap.get(each.getPlanId()), each.getId(), each.getStartTime());
                if (CollectionUtil.isEmpty(taskAssigns)) {
                    // 未分配到人，直接删除
                    removeById(each.getId());
                    continue;
                }
                assigns.addAll(taskAssigns);
                unFinishMappings.add(unfinishTaskService.genUnFinishMappingByTask(each));
            }
        }
        if (CollectionUtil.isNotEmpty(assigns)) {
            assignService.batchSave(assigns,1000);
        }
        if (CollectionUtil.isNotEmpty(unFinishMappings)) {
            unfinishTaskService.batchSave(unFinishMappings, 1000);
        }
        log.error("patrol schedule produce task end...");
    }

    void produceSegment(List<PatrolCheckPlan> plans, Date currDate, List<PatrolTaskAssign> assigns, List<PatrolUnfinishTask> unFinishMappings, List<PatrolTask> tasks) {
        for (PatrolCheckPlan plan : plans) {
            try {
                if (todayUnProduce(plan)) {
                    // 今日未生成，开始检查周期
                    CycleType cycleType = CycleType.find(plan.getCycleType());
                    switch (cycleType) {
                        case CYCLE:
                            // 固定周期
                            produceForCycle(plan, currDate, assigns, unFinishMappings, tasks);
                            break;
                        case SINGLE:
                            // 单次临时任务生成
                            produceForSingle(plan, currDate, assigns, unFinishMappings, tasks);
                            break;
                        case SPECIFY:
                            // 指定次数
                            produceForSpecify(plan, currDate, assigns, unFinishMappings, tasks);
                            break;
                    }
                }
            } catch (Exception e) {
                log.error("智能巡检任务生成出错，计划id：{}-{}", plan.getId(), e);
            }
        }
        // 更新计划信息
        planService.saveOrUpdateBatch(plans);
    }

    /**
     * 今日是否已经生成
     *
     * @param plan
     * @return
     */
    boolean todayUnProduce(PatrolCheckPlan plan) {
        return plan.getLastProduceTime() == null || DateUtil.beginOfDay(new Date()).after(plan.getLastProduceTime());
    }

    /**
     * 单次临时
     *
     * @param plan
     * @param currDate
     * @param assigns
     * @param unFinishMappings
     */
    void produceForSingle(PatrolCheckPlan plan, Date currDate, List<PatrolTaskAssign> assigns, List<PatrolUnfinishTask> unFinishMappings, List<PatrolTask> tasks) {
        Date startTime = plan.getPlanStartTime();
        Date endTime = plan.getPlanEndTime();
        // 从未生成，且计划开始时间早于今日最后一刻
        if (plan.getLastProduceTime() == null && DateUtil.endOfDay(currDate).after(plan.getPlanStartTime())) {
            doProduceTask(startTime, endTime, plan, currDate, assigns, unFinishMappings, tasks);
        }
    }

    /**
     * 指定次数
     *
     * @param plan
     * @param currDate
     * @param assigns
     * @param unFinishMappings
     */
    void produceForSpecify(PatrolCheckPlan plan, Date currDate, List<PatrolTaskAssign> assigns, List<PatrolUnfinishTask> unFinishMappings, List<PatrolTask> tasks) {
        CycleUnit cycleUnit = CycleUnit.find(plan.getCycleUnit());
        switch (cycleUnit) {
            case D:
                produceSpecify4Day(plan, currDate, assigns, unFinishMappings, tasks);
                break;
            case W:
                produceSpecify4Week(plan, currDate, assigns, unFinishMappings, tasks);
                break;
            case M:
                produceSpecify4Month(plan, currDate, assigns, unFinishMappings, tasks);
                break;
        }
    }

    /**
     * 固定周期
     *
     * @param plan
     * @param currDate
     * @param assigns
     * @param unFinishMappings
     */
    void produceForCycle(PatrolCheckPlan plan, Date currDate, List<PatrolTaskAssign> assigns, List<PatrolUnfinishTask> unFinishMappings, List<PatrolTask> tasks) {
        CycleUnit cycleUnit = CycleUnit.find(plan.getCycleUnit());
        switch (cycleUnit) {
            case H:
                produceCycle4Hour(plan, currDate, assigns, unFinishMappings, tasks);
                break;
            case D:
                produceCycle4Day(plan, currDate, assigns, unFinishMappings, tasks);
                break;
            case W:
                produceCycle4Week(plan, currDate, assigns, unFinishMappings, tasks);
                break;
            case M:
                produceCycle4Month(plan, currDate, assigns, unFinishMappings, tasks);
                break;
        }
    }

    /**
     * 指定次数-天
     *
     * @param plan
     * @param currDate
     * @param assigns
     * @param unFinishMappings
     */
    void produceSpecify4Day(PatrolCheckPlan plan, Date currDate, List<PatrolTaskAssign> assigns, List<PatrolUnfinishTask> unFinishMappings, List<PatrolTask> tasks) {
        Date todayStart = DateUtil.beginOfDay(currDate);
        Date lastProduceDate = plan.getLastProduceTime();
        // 从未生成过 或 上次生成时间早于今天第一刻（00:00）
        if (lastProduceDate == null || DateUtil.beginOfDay(currDate).after(lastProduceDate)) {
            // "8-10,12-14,16-18"
            String periodStr = plan.getPeriod();
            // ["8-10","12-14","16-18"]
            String[] periodArr = periodStr.split(Constant.SPLIT_COMMA);
            for (String period : periodArr) {
                // ["8","10"]...
                String[] hour = period.split(Constant.SPLIT_HYPHEN);
                Date startDate = DateUtil.offsetHour(todayStart, Integer.parseInt(hour[0]));
                Date endDate = DateUtil.offsetHour(todayStart, Integer.parseInt(hour[1]));
                // 已过去的不生成
                if (endDate.before(currDate)) {
                    continue;
                }
                doProduceTask(startDate, endDate, plan, currDate, assigns, unFinishMappings, tasks);
            }
        }
    }

    /**
     * 指定次数-周
     *
     * @param plan
     * @param currDate
     * @param assigns
     * @param unFinishMappings
     */
    void produceSpecify4Week(PatrolCheckPlan plan, Date currDate, List<PatrolTaskAssign> assigns, List<PatrolUnfinishTask> unFinishMappings, List<PatrolTask> tasks) {
        Date lastProduceDate = plan.getLastProduceTime();
        if (lastProduceDate == null || DateUtil.beginOfDay(currDate).after(lastProduceDate)) {
            String periodStr = plan.getPeriod();
            List<Integer> periodList = Arrays.asList(periodStr.split(Constant.SPLIT_COMMA)).stream().map(Integer::new).collect(Collectors.toList());
            // 当前是周几，周日：1， 周一：2  ...
            Integer currDayOfWeek = DateUtil.dayOfWeek(currDate);
            if (periodList.contains(currDayOfWeek)) {
                // 任务当天0点开始，24点结束
                DateTime startDate = DateUtil.beginOfDay(currDate);
                DateTime endDate = DateUtil.endOfDay(currDate);
                doProduceTask(startDate, endDate, plan, currDate, assigns, unFinishMappings, tasks);
            }
        }
    }

    /**
     * 指定次数-月
     *
     * @param plan
     * @param currDate
     * @param assigns
     * @param unFinishMappings
     */
    void produceSpecify4Month(PatrolCheckPlan plan, Date currDate, List<PatrolTaskAssign> assigns, List<PatrolUnfinishTask> unFinishMappings, List<PatrolTask> tasks) {
        Date lastProduceDate = plan.getLastProduceTime();
        if (lastProduceDate == null || DateUtil.beginOfDay(currDate).after(lastProduceDate)) {
            String periodStr = plan.getPeriod();
            List<Integer> periodList = Arrays.asList(periodStr.split(Constant.SPLIT_COMMA)).stream().map(Integer::new).collect(Collectors.toList());
            // 当前是本月几号 一号:1， 二号:2 ...
            Integer currDayOfMonth = DateUtil.dayOfMonth(currDate);
            if (periodList.contains(currDayOfMonth)) {
                // 任务当天0点开始，24点结束
                DateTime startDate = DateUtil.beginOfDay(currDate);
                DateTime endDate = DateUtil.endOfDay(currDate);
                // 已过去的不生成
                if (endDate.before(endDate)) {
                    return;
                }
                doProduceTask(startDate, endDate, plan, currDate, assigns, unFinishMappings, tasks);

            }
        }
    }

    /**
     * 周期性-时
     *
     * @param plan
     * @param currDate
     */
    void produceCycle4Hour(PatrolCheckPlan plan, Date currDate, List<PatrolTaskAssign> assigns, List<PatrolUnfinishTask> unFinishMappings, List<PatrolTask> tasks) {
        DateTime todayStartTime = DateUtil.beginOfDay(currDate);
        // 当前所在小时数。2022-03-01 09:23:00 -->  9
        int hour = DateUtil.hour(currDate, true);
        Date planStartTime = plan.getPlanStartTime();
        Integer interval = plan.getFrequencyInterval();
        // 检查周期设置，防止死循环
        checkInterval(plan.getId(),interval);
        if (planStartTime != null) {
            // 指定了计划开始时间，从计划开始时间生成，如已过去，则从当前时间开始生成
            // 如指定8点开始，此时已经9点，则从9点开始生成
            int planStartHour = DateUtil.hour(plan.getPlanStartTime(), true);
            // 如果当前时间早于计划开始时间，则从计划开始时间生成。
            // 如果当前时间晚于计划时间，则找到当前时间可做（截止时间早于当前时间）的那个周期开始生成
            if (hour < planStartHour) {
                hour = planStartHour;
            } else {
                while (planStartHour < 24) {
                    // 时任务重，开始时间 + 周期 即是任务截止时间
                    if (planStartHour + interval > hour) {
                        hour = planStartHour;
                        break;
                    }
                    planStartHour += interval;
                }
            }

        }
        // 从hour时开始时间生成
        while (hour < 24) {
            Date taskStartTime = DateUtil.offsetHour(todayStartTime, hour);
            Date taskEndTime = DateUtil.offsetHour(taskStartTime, interval);
            doProduceTask(taskStartTime, taskEndTime, plan, currDate, assigns, unFinishMappings, tasks);
            hour += interval;
        }
    }

    /**
     * 周期性-天
     *
     * @param plan
     * @param currDate
     */
    void produceCycle4Day(PatrolCheckPlan plan, Date currDate, List<PatrolTaskAssign> assigns, List<PatrolUnfinishTask> unFinishMappings, List<PatrolTask> tasks) {
        Date todayStartTime = DateUtil.beginOfDay(currDate);
        Date lastProduceTime = plan.getLastProduceTime();
        Date planStartTime = plan.getPlanStartTime();
        Integer interval = plan.getFrequencyInterval();
        // 检查周期设置，防止死循环
        checkInterval(plan.getId(),interval);
        // 计划开始时间为今天或今天之前
        if ((planStartTime.before(currDate)) || planStartTime.equals(currDate)) {
            // 从未生成
            if (lastProduceTime == null) {
                Date taskStartTime = planStartTime;
                // 如果当前时间晚于计划时间，则找到当前时间可做（截止时间早于当前时间）的那个周期开始生成
                while (DateUtil.offsetDay(taskStartTime, interval).before(currDate)) {
                    taskStartTime = DateUtil.offsetDay(taskStartTime, interval);
                }
                Date taskEndTime = DateUtil.offsetDay(taskStartTime, interval);
                doProduceTask(taskStartTime, taskEndTime, plan, currDate, assigns, unFinishMappings, tasks);
            } else {
                // 上次生成时间+周期天数，如果早于或等于今日，则今日需生成
                DateTime nextGenTime = DateUtil.beginOfDay(DateUtil.offsetDay(lastProduceTime, interval));
                if (nextGenTime.before(todayStartTime) || nextGenTime.equals(todayStartTime)) {
                    Date taskStartTime = todayStartTime;
                    // 处理禁用了很久之后重新开启的情况，如果计划禁用后抹掉lastProduceTime，则无需此段代码
                    // while (DateUtil.offsetDay(planStartTime, interval).before(currDate)) {
                    //     taskStartTime = DateUtil.offsetDay(taskStartTime, interval);
                    // }
                    Date taskEndTime = DateUtil.offsetDay(taskStartTime, interval);
                    doProduceTask(taskStartTime, taskEndTime, plan, currDate, assigns, unFinishMappings, tasks);
                }
            }
        }
    }

    /**
     * 周期性-周
     *
     * @param plan
     * @param currDate
     */
    void produceCycle4Week(PatrolCheckPlan plan, Date currDate, List<PatrolTaskAssign> assigns, List<PatrolUnfinishTask> unFinishMappings, List<PatrolTask> tasks) {
        Date planStartTime = plan.getPlanStartTime();
        Date lastProduceTime = plan.getLastProduceTime();
        Date todayStartTime = DateUtil.beginOfDay(currDate);
        Integer interval = plan.getFrequencyInterval();
        // 检查周期设置，防止死循环
        checkInterval(plan.getId(),interval);
        // 今天或今天之前开始
        if (planStartTime.before(currDate) || planStartTime.equals(todayStartTime)) {
            // 以前从未生成
            if (lastProduceTime == null) {
                Date taskStartTime = planStartTime;
                // 如果当前时间晚于计划时间，则找到当前时间可做（截止时间早于当前时间）的那个周期开始生成
                while (DateUtil.offsetWeek(taskStartTime, interval).before(currDate)) {
                    taskStartTime = DateUtil.offsetWeek(taskStartTime, interval);
                }
                Date taskEndTime = DateUtil.offsetWeek(taskStartTime, interval);
                doProduceTask(taskStartTime, taskEndTime, plan, currDate, assigns, unFinishMappings, tasks);
            } else {
                // 上次生成时间+周期天数，如果早于或等于今日，则今日需生成
                DateTime nextGenTime = DateUtil.beginOfDay(DateUtil.offsetWeek(lastProduceTime, interval));
                if (nextGenTime.before(todayStartTime)) {
                    // 禁用很久的情况，需找到最近的可做周期开始生成
                    Date taskStartTime = planStartTime;
                    while (DateUtil.offsetWeek(taskStartTime, interval).before(currDate)) {
                        taskStartTime = DateUtil.offsetWeek(taskStartTime, interval);
                    }
                    Date taskEndTime = DateUtil.offsetWeek(taskStartTime, interval);
                    doProduceTask(taskStartTime, taskEndTime, plan, currDate, assigns, unFinishMappings, tasks);
                } else if (nextGenTime.equals(todayStartTime)) {
                    // 正常连续生成，直接用今日时间做开始时间
                    Date taskStartTime = todayStartTime;
                    Date taskEndTime = DateUtil.offsetWeek(taskStartTime, interval);
                    doProduceTask(taskStartTime, taskEndTime, plan, currDate, assigns, unFinishMappings, tasks);
                }
            }
        }

    }

    /**
     * 周期性-月
     *
     * @param plan
     * @param currDate
     */
    void produceCycle4Month(PatrolCheckPlan plan, Date currDate, List<PatrolTaskAssign> assigns, List<PatrolUnfinishTask> unFinishMappings, List<PatrolTask> tasks) {
        Date planStartTime = plan.getPlanStartTime();
        Date lastProduceTime = plan.getLastProduceTime();
        Date todayStartTime = DateUtil.beginOfDay(currDate);
        Integer interval = plan.getFrequencyInterval();
        // 检查周期设置，防止死循环
        checkInterval(plan.getId(),interval);
        // 今天或今天之前开始
        if (planStartTime.before(currDate) || planStartTime.equals(todayStartTime)) {
            // 以前从未生成
            if (lastProduceTime == null) {
                Date taskStartTime = planStartTime;
                // 如果当前时间晚于计划时间，则找到当前时间可做（截止时间早于当前时间）的那个周期开始生成
                while (DateUtil.offsetMonth(taskStartTime, interval).before(currDate)) {
                    taskStartTime = DateUtil.offsetMonth(taskStartTime, interval);
                }
                Date taskEndTime = DateUtil.offsetMonth(taskStartTime, interval);
                doProduceTask(taskStartTime, taskEndTime, plan, currDate, assigns, unFinishMappings, tasks);
            } else {
                // 上次生成时间+周期天数，如果早于或等于今日，则今日需生成
                DateTime nextGenTime = DateUtil.beginOfDay(DateUtil.offsetMonth(lastProduceTime, interval));
                if (nextGenTime.before(todayStartTime)) {
                    // 禁用很久的情况，需找到最近的可做周期开始生成
                    Date taskStartTime = planStartTime;
                    while (DateUtil.offsetMonth(taskStartTime, interval).before(currDate)) {
                        taskStartTime = DateUtil.offsetMonth(taskStartTime, interval);
                    }
                    Date taskEndTime = DateUtil.offsetMonth(taskStartTime, interval);
                    doProduceTask(taskStartTime, taskEndTime, plan, currDate, assigns, unFinishMappings, tasks);
                } else if (nextGenTime.equals(todayStartTime)) {
                    // 正常连续生成，直接用今日时间做开始时间
                    Date taskStartTime = todayStartTime;
                    Date taskEndTime = DateUtil.offsetMonth(taskStartTime, interval);
                    doProduceTask(taskStartTime, taskEndTime, plan, currDate, assigns, unFinishMappings, tasks);
                }
            }
        }

    }

    /**
     * 执行生成任务的动作，并生成对应的taskAssign和unfinishTask列表
     *
     * @param startTime
     * @param endTime
     * @param plan
     */
    void doProduceTask(Date startTime, Date endTime, PatrolCheckPlan plan, Date currDate, List<PatrolTaskAssign> assigns, List<PatrolUnfinishTask> unFinishMappings, List<PatrolTask> tasks) {
        Integer delay = plan.getDelayMinute();
        Integer advance = plan.getAdvanceTime();
        PatrolTask task = new PatrolTask();
        task.setStartTime(startTime)
                .setEndTime(endTime)
                // 允许补检时间
                .setDeadLine(DateUtil.offsetMinute(endTime, delay))
                // 提前时间
                .setActiveTime(DateUtil.offsetMinute(startTime, advance == null ? 0 : (-1 * advance)))
                .setPlanId(plan.getId())
                .setStatus(TaskStatus.UN_FINISH)
                .setExcepHandel(ExceptProcessStatus.UN_REQUIRED_PROCESS);
        task.setOrgCode(plan.getOrgCode());
        task.setTenentId(plan.getTenentId());
        tasks.add(task);
        plan.setLastProduceTime(startTime);
    }

    void checkInterval(Long planId, Integer interval) {
        if (Integer.valueOf(0).equals(interval) || interval == null) {
            log.error("智能巡检->不支持的周期类型。计划id：{}", planId);
            throw new RuntimeException();
        }
    }

    void batchSave(List<PatrolTask> taskList, Integer batchSize) {
        List<List<PatrolTask>> taskPages = Lists.partition(taskList, batchSize);
        taskPages.forEach(taskPage -> {
            taskMapper.batchInsert(taskPage);
        });
    }

    //</editor-fold>


    @Override
    public Page<AppTaskDto> appGetTaskList(Long pageNo, Long pageSize) {
        Long userId = Context.getCurrentUserId();
        Date currDate = new Date();
        DateTime endOfToday = DateUtil.endOfDay(currDate);
        DateTime startOfToday = DateUtil.beginOfDay(currDate);
        Page<AppTaskDto> page = new Page<>(pageNo + 1, pageSize);
        // 通过userId查询，无需过滤租户
        Page<AppTaskDto> appTaskPage = taskMapper.selectTaskByUserAndDate(page, startOfToday, endOfToday, userId);
        if (appTaskPage != null && appTaskPage.getRecords() != null) {
            appTaskPage.getRecords().forEach(record -> {
                Long planId = record.getPlanId();
                PatrolCheckPlan plan = planService.getById(planId);
                if (plan != null) {
                    CycleType cycleType = CycleType.find(plan.getCycleType());
                    if (cycleType != null) {
                        String endDate = DateTimeUtil.formatDate(record.getTaskEndTime(), "yyyy/MM/dd HH:mm");
                        String startDate = DateTimeUtil.formatDate(record.getTaskStartTime(), "yyyy/MM/dd HH:mm");
                        if (CycleType.CYCLE.equals(cycleType)) {
                            record.setCycleTypeDesc(plan.getCycleDesc());

                            if (record.getTaskEndTime().getTime() - record.getTaskStartTime().getTime() >= 24L * 3600 * 1000) {
                                String endDateStr = endDate.substring(0, 10);
                                String startDateStr = startDate.substring(0, 10);
                                record.setTempTimePeriod(startDateStr + " - " + endDateStr);
                            } else {
                                String endDateStr = endDate.substring(11, 16);
                                String startDateStr = startDate.substring(11, 16);
                                record.setTempTimePeriod(startDateStr + " - " + endDateStr);
                            }
                        } else if (CycleType.SPECIFY.equals(cycleType)) {
                            record.setCycleTypeDesc(plan.getCycleDesc());
                            record.setTempTimePeriod(startDate + " - " + endDate);
                        } else if (CycleType.SINGLE.equals(cycleType)) {
                            // 允许补检，截止时间增加
                            if ("1".equals(record.getAllowRepair()) && record.getSupplementaryInspectionTime() != null) {
                                Date endTime = DateUtil.offsetMinute(plan.getPlanEndTime(), record.getSupplementaryInspectionTime());
                                endDate = DateTimeUtil.formatDate(endTime, "yyyy/MM/dd HH:mm");
                            }
                            // 允许提前，截止时间减少
                            if ("1".equals(record.getSignAdvance()) && record.getAdvanceTime() != null) {
                                Date startTime = DateUtil.offsetMinute(plan.getPlanStartTime(), -record.getAdvanceTime());
                                startDate = DateTimeUtil.formatDate(startTime, "yyyy/MM/dd HH:mm");
                            }
                            record.setCycleTypeDesc(cycleType.getDesc());
                            record.setTempTimePeriod(startDate + " - " + endDate);
                        }

                    }
                }
                Long pointNum = patrolPlanPointMappingService.getNumByPlanId(planId);
                record.setCheckPointSize(pointNum);
            });
        }
        return appTaskPage;
    }

    @Override
    public Page<WebTaskDto> webGetTaskList(TaskSearchBean searchBean) {
        Page<WebTaskDto> page = new Page<>(searchBean.getPage() + 1, searchBean.getSize());

        if (StringUtils.isNotBlank(searchBean.getCheckUserName())) {
            List<Long> checkUseIds = userDao.getIdsByNameLike(searchBean.getCheckUserName());
            if (CollectionUtil.isEmpty(checkUseIds)) {
                return new Page<>();
            }
            searchBean.setCheckUserIds(checkUseIds);
        }

        Set<Long> authDeparts = null;
        authDeparts = authService.findAuthDepartIdsByCurUser();
        if (CollectionUtil.isEmpty(authDeparts)) {
            return new Page<>();
        }

//        Page<WebTaskDto> taskPage = taskMapper.selectTask(page, authDeparts, searchBean);
        Page<WebTaskDto> taskPage = taskMapper.selectNewTask(page, authDeparts, searchBean);
        convertTaskList(taskPage.getRecords());
        return taskPage;
    }

    /**
     * web任务页面列表字段处理
     *
     * @param taskList
     */
    void convertTaskList(List<WebTaskDto> taskList) {
        if (CollectionUtil.isNotEmpty(taskList)) {
            Set<Long> recordIds = taskList.stream().map(WebTaskDto::getTaskId).collect(Collectors.toSet());
            List<TaskAssignDto> taskAssigns = assignService.getAssignByTaskIds(recordIds);
            if(CollectionUtil.isNotEmpty(taskAssigns)){
                Map<Long, List<TaskAssignDto>> taskAssignMap = taskAssigns.stream().collect(Collectors.groupingBy(TaskAssignDto::getTaskId));
                SimpleDateFormat dateFormatter = SimpleDateFormatCache.getYmdhms();
                taskList.forEach(each -> {
                    Date startTime = each.getTaskStartTime();
                    if (startTime != null) {
                        String dateTimeStr = dateFormatter.format(startTime);
                        String[] dateTimeStrs = dateTimeStr.split(Constant.SPLIT_SPACE);
                        each.setCheckDate(dateTimeStrs[0]);
                        each.setCheckTime(dateTimeStrs[1]);
                    }
                    // 任务分配人列表处理
                    List<TaskAssignDto> assigns = taskAssignMap.get(each.getTaskId());
                    if (CollectionUtil.isNotEmpty(assigns)) {
                        List<SimpleListBean> checkUserList = new ArrayList<>();
                        assigns.forEach(assign->{
                            SimpleListBean simpleListBean =new SimpleListBean();
                            simpleListBean.setId(assign.getUserId());
                            simpleListBean.setName(assign.getUsername());
                            checkUserList.add(simpleListBean);
                        });
                        each.setCheckUserList(checkUserList);
                        String assignNames = assigns.stream().map(TaskAssignDto::getUsername).collect(Collectors.joining(Constant.SPLIT_COMMA));
                        each.setCheckUserNames(assignNames);
                    }
                });
            }
        }
    }

    void convertTaskRecordList(List<WebTaskDto> taskList) {
        if (CollectionUtil.isNotEmpty(taskList)) {
            // 准备数据
            // 未检的任务，checkUser为空，需到assign表中取巡检人员
            Set<Long> taskIds = taskList.stream().filter(e -> e.getCheckUser() == null).map(WebTaskDto::getTaskId).collect(Collectors.toSet());
            List<TaskAssignDto> taskAssigns = CollectionUtil.isEmpty(taskIds) ? new ArrayList<>() : assignService.getAssignByTaskIds(taskIds);
            Map<Long, List<TaskAssignDto>> taskAssignMapper = taskAssigns.stream().collect(Collectors.groupingBy(TaskAssignDto::getTaskId));
            // 正常完成的任务，checkUser有值，直接取此作为巡检人员
            Set<Long> checkUserIds = taskList.stream().map(WebTaskDto::getCheckUser).filter(e -> Objects.nonNull(e)).collect(Collectors.toSet());
            List<BaseUser> checkUsers = CollectionUtil.isEmpty(checkUserIds) ? new ArrayList<>() : userDao.getAllByIds(checkUserIds);
            Map<Long, String> checkUserMapper = checkUsers.stream().collect(Collectors.toMap(BaseUser::getId, BaseUser::getRealName));
            List<Long> departIds = taskList.stream().map(WebTaskDto::getRespDepartId).collect(Collectors.toList());
            List<BpDepart> list = departDao.findByIds(departIds);
            Map<Long, String> departMap = list.stream().collect(Collectors.toMap(BpDepart::getId, BpDepart::getName));
            taskList.forEach(each -> {
                if (each.getRespDepartId() != null) {
                    each.setRespDepartName(departMap.get(each.getRespDepartId()));
                }
                if (each.getCheckUser() != null) {
                    // 有完成人，直接展示任务完成人（task表中记录的是最后一个检查点的记录人）
                    each.setCheckUserName(checkUserMapper.get(each.getCheckUser()));
                } else {
                    // 无完成人，表示此任务未检，展示所有分配到的人
                    List<TaskAssignDto> taskAssignList = taskAssignMapper.get(each.getTaskId());
                    List<SimpleListBean> assigns = new ArrayList<>();
                    taskAssignList.forEach(e -> {
                        assigns.add(new SimpleListBean(e.getUserId(),e.getUsername()));
                    });
                    String assignNames = taskAssignList.stream().map(TaskAssignDto::getUsername).collect(Collectors.joining(Constant.SPLIT_COMMA));
                    each.setCheckUserName(assignNames);
                }
                if (each.getTimeCost() != null) {
                    each.setTimeCostStr(DateUtil.formatBetween(each.getTimeCost(), BetweenFormatter.Level.SECOND));
                }
                if (CheckResultStatus.ABNORMAL.equals(each.getCheckResult()) && StringUtils.isNotBlank(each.getExceptReason())) {
                    each.setCheckResultInfo(
                            new StringBuilder()
                                    .append(each.getCheckResult())
                                    .append(Constant.EN_LEFT_BRACKETS)
                                    .append(each.getExceptReason())
                                    .append(Constant.EN_RIGHT_BRACKETS)
                                    .toString()
                    );
                } else {
                    each.setCheckResultInfo(each.getCheckResult());
                }
            });
        }
    }

    @Override
    public Page<WebTaskDto> getTaskHadDone(TaskRecordSearchBean searchBean) {
        Long userId = Context.getCurrentUserId();
        buildAppQuery(searchBean);
        Set<Long> authDeparts = null;
        if (RequestBy.WEB.getValue().equals(searchBean.getReqBy())) {
            // 查询请求来自web，查（权限内）所有人的。app查询时只查本人的，使用userId参数
            userId = null;
            authDeparts = authService.findAuthDepartIdsByCurUser();
            if (CollectionUtil.isEmpty(authDeparts)) {
                return new Page<>();
            }
            // web，按人员模糊查询
            if (StringUtils.isNotBlank(searchBean.getCheckUserName())) {
                List<Long> checkUserIds = userDao.getIdsByNameLike(searchBean.getCheckUserName());
                if (CollectionUtil.isNotEmpty(checkUserIds)) {
                    searchBean.setCheckUserIds(checkUserIds);
                } else {
                    // 查无此人
                    return new Page<>();
                }
            }
        }
        Page<WebTaskDto> page = new Page<>(searchBean.getPage() + 1, searchBean.getSize());
        Page<WebTaskDto> resultPage = taskMapper.selectFinishTask(page, userId, authDeparts, searchBean);
        convertTaskRecordList(resultPage.getRecords());
        return resultPage;
    }

    @Override
    public Integer getTaskHadDoneCount(TaskRecordSearchBean searchBean) {
        Long userId = Context.getCurrentUserId();
        buildAppQuery(searchBean);
        Set<Long> authDeparts = null;
        if (RequestBy.WEB.getValue().equals(searchBean.getReqBy())) {
            // 查询请求来自web，查（权限内）所有人的。app查询时只查本人的，使用userId参数
            userId = null;
            authDeparts = authService.findAuthDepartIdsByCurUser();
            if (CollectionUtil.isEmpty(authDeparts)) {
                return 0;
            }
            // web，按人员模糊查询
            if (StringUtils.isNotBlank(searchBean.getCheckUserName())) {
                List<Long> checkUserIds = userDao.getIdsByNameLike(searchBean.getCheckUserName());
                if (CollectionUtil.isNotEmpty(checkUserIds)) {
                    searchBean.setCheckUserIds(checkUserIds);
                } else {
                    // 查无此人
                    return 0;
                }
            }
        }
//        Page<WebTaskDto> page = new Page<>(searchBean.getPage() + 1, searchBean.getSize());
        Integer count = taskMapper.selectFinishTaskCount(userId, authDeparts, searchBean);

        return count;
    }

    void buildAppQuery(TaskRecordSearchBean searchBean) {
        if (StringUtils.isNotBlank(searchBean.getAppDateQuery())) {
            Date currDate = new Date();
            if ("week".equals(searchBean.getAppDateQuery())) {
                // 本周
                searchBean.setFinishTimeStart(DateUtil.beginOfWeek(currDate));
                searchBean.setFinishTimeEnd(currDate);
            } else if ("month".equals(searchBean.getAppDateQuery())) {
                // 本月
                searchBean.setFinishTimeStart(DateUtil.beginOfMonth(currDate));
                searchBean.setFinishTimeEnd(currDate);
            } else if ("lastMonth".equals(searchBean.getAppDateQuery())) {
                // 上月
                DateTime lastMonthCurr = DateUtil.offsetMonth(currDate, -1);
                searchBean.setFinishTimeStart(DateUtil.beginOfMonth(lastMonthCurr));
                searchBean.setFinishTimeEnd(DateUtil.endOfMonth(lastMonthCurr));
            }
        }
    }

    @Override
    public List<AppCheckPointDto> appGetCheckPointByTaskId(Long taskId) {
        List<AppCheckPointDto> checkPoints = taskMapper.selectCheckPointByTask(taskId);
        return checkPoints;
    }

    public List<SimplePointRecordDto> appGetCheckPointRecordByTaskId(Long taskId) {
        List<SimplePointRecordDto> pointRecord = recordService.appGetPointRecord(taskId);
        return pointRecord;
    }

    @Override
    public CheckPointDetailVo getItemByPoint(Long pointId) {
        CheckPointDetailVo checkItemList = pointService.getCheckPointByPointIdForApp(pointId);
        return checkItemList;
    }

    @Override
    public CheckPointDetailVo getNewItemByPoint(Long pageNo, Long pageSize,Long pointId) {
        Long userId = Context.getCurrentUserId();
        Page<CheckItemVo> page = new Page<>(pageNo + 1, pageSize);
        //巡检任务列表
        CheckPointDetailVo checkItemList = pointService.getCheckPointByPointIdForApp(pointId);
        List<CheckItemVo> vos = checkItemList.getCheckItemVoList();
        vos.forEach(checkItemVo -> checkItemVo.setCategory("巡检"));
        if (checkItemList.getDistrictId()!=null) {
            List<AppCheckTaskVo.PointTaskGroup> taskGroupList = bpInvestigateTaskService.selectByDistrictId(checkItemList.getDistrictId(), userId);
            if (CollectionUtils.isNotEmpty(taskGroupList)) {
                for (AppCheckTaskVo.PointTaskGroup group : taskGroupList) {
                    CheckItemVo vo = new CheckItemVo();
                    BeanUtils.copyProperties(group,vo);
                    vo.setCategory("隐患排查");
                    vos.add(vo);
                }
            }
        }

        int start = 0;
        int end = 0;
        if (pageNo == 0) {
            start = 0;
            end = pageSize.intValue()>vos.size()?vos.size():pageSize.intValue();
        } else {
            start = (int)((pageNo + 1) * pageSize);
            end = start+ pageSize.intValue()>vos.size()?vos.size():start+ pageSize.intValue();
        }
        List<CheckItemVo> list=new ArrayList<>();
        if (start<=end){
            list=vos.subList(start,end);
        }
        page.setTotal(vos.size());
        page.setRecords(list);
        checkItemList.setCheckItemVoList(null);
        checkItemList.setCheckItemVoPage(page);
        return checkItemList;
    }

    @Override
    public WebTaskCheckVo getItemByTask(Long taskId) {
        WebTaskCheckVo webTaskCheckVo = new WebTaskCheckVo();
        PatrolTask task = this.getById(taskId);
        webTaskCheckVo.setTaskStartTime(task.getStartTime());
        webTaskCheckVo.setPlanName(planService.getNameByTaskId(taskId));
        // 获取未完成的检查点
        List<Long> unCheckpointIds = taskMapper.getPointIdsByTaskId(taskId, false);
        List<CheckPointDetailVo> uncheckPointList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(unCheckpointIds)) {
            unCheckpointIds.forEach(e -> {
                uncheckPointList.add(getItemByPoint(e));
            });
        }
        webTaskCheckVo.setUnCheckPointList(uncheckPointList);
        // 获取已完成的检查记录
        List<WebTaskRecordPointVo> pointRecords = recordService.webGetPointRecord(taskId);
        webTaskCheckVo.setFinishPointList(pointRecords);
        return webTaskCheckVo;
    }

    @Override
    public List<SimpleMonitoringPointBean> getMonitorByDevice(Long deviceId) {
        /**
         * deviceId 是 wwyt_bp_device_facility_collection 中的id，
         * wwyt_bp_monitoring_point_rel 中的deviceId是 collection 中的 deviceId
         */
        BpDeviceFacilityCollection collection = collectionDao.getCollectionIdById(deviceId);
        if (collection == null) {
            // 判读是否是自定义对象
            return new ArrayList<>();
        }
        Long deviceRefId = collection.getDeviceId();
        List<SimpleMonitoringPointBean> resultList = new ArrayList<>();
        DeviceTypeEnum deviceType = DeviceTypeEnum.find(collection.getDeviceTypeName());
        if (deviceType == null) {
            return new ArrayList<>();
        }
        List<BpMonitoringPointRel> deviceMonitorMapping = relDao.findByRefTypeAndRefId(deviceType.getValue(), deviceRefId);
        for (BpMonitoringPointRel mapping : deviceMonitorMapping) {
            MhMonitoringPoint mPoint = mhMonitoringPointDao.getOne(mapping.getMonitoringPointId());
            MhPointValueAndSwitchStateBean stateBean = mhMonitoringRealTimeService.getValueAndSwitchgStateBean(mPoint,Context.getCurrentTenantId(), Context.getCompanyCode());
            SimpleMonitoringPointBean pointBean = new SimpleMonitoringPointBean(
                    mPoint.getId(),
                    mPoint.getPointCode(),
                    mPoint.getName(),
                    mPoint.getHighValue2(),
                    mPoint.getHighValue1(),
                    mPoint.getLowValue1(),
                    mPoint.getLowValue2(),
                    mPoint.getUnit(),
                    stateBean.getConnectState()
            );
            resultList.add(pointBean);
        }
        return resultList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long appDoTask(AppFinishPointBean pointBean) throws ApiBusinessException {
        if (pointBean.getRecordId() == null && hasBeenSubmit(pointBean)) {
            throw new IllegalArgumentException("此检查点已被提交，请刷新页面！");
        }
        // 任务下无检查点
        if (pointBean.getPointId() == null) {
            log.info("app do task with null point");
            dealTaskWithNullPoint(pointBean);
            return null;
        }
        JSONObject lastLocationBean = new JSONObject();
        if (StringUtils.isEmpty(pointBean.getVerifyLocation())) {
            // 找最近一次定位
            Optional<BaseUser> op = baseUserDao.findById(Context.getCurrentUserId());
            if (op.isPresent()) {
                BaseUser baseUser = op.get();
                try {
                    if (StringUtils.isNotEmpty(baseUser.getSn())) {
                        String sn = baseUser.getSn();
                        String taskCode = pointBean.getTaskId() + "";
                        TrajectoryLastLocationResp lastLocation = personTrajectoryService.getLastLocation(sn, taskCode, IdUtil.simpleUUID().toString());
                        if (lastLocation != null) {
                            //如果最近一次点位在配置的时间内(10分钟) 则用最近一个点位的时间往前推60秒，看这段时间的点位是否在区域内
                            if (System.currentTimeMillis() - lastLocation.getTimestampMillisecond() <= getMaxTimeRange()) {
                                lastLocationBean.put("x", lastLocation.getLongitude());
                                lastLocationBean.put("y", lastLocation.getLatitude());
                                lastLocationBean.put("floorNo", lastLocation.getFloorNo());
                                lastLocationBean.put("time", lastLocation.getTimestampMillisecond());
                            }
                        }
                    }
                } catch (Exception e) {
                    log.error("query last location error", e);
                }
            }
        }
        // 保存record
        Long taskId = pointBean.getTaskId();
        PatrolTask task = this.getById(taskId);
        Integer recordCountBefore = taskMapper.countRecord(taskId);
        PatrolRecord patrolRecord = new PatrolRecord();
        patrolRecord.setId(pointBean.getRecordId())
                .setTaskId(pointBean.getTaskId())
                .setPlanId(task.getPlanId())
                .setPointId(pointBean.getPointId())
                .setPointName(pointBean.getPointName())
                .setReportWay(pointBean.getReportWay())
                .setVerifyType(pointBean.getVerifyType())
                .setVerifyState(pointBean.getVerifyState())
                .setVerifyLocation(StringUtils.isNotEmpty(pointBean.getVerifyLocation()) ? pointBean.getVerifyLocation() : lastLocationBean.toJSONString())
                .setCheckUserId(pointBean.getCheckUserId() == null ? Context.getCurrentUserId() : pointBean.getCheckUserId())
                .setCheckStartTime(pointBean.getStartTime())
                .setCheckEndTime(pointBean.getEndTime())
                .setCheckResult(CheckResultStatus.NORMAL);
        // 保存record（对应一个检查点）
        recordService.saveOrUpdate(patrolRecord);
        AtomicInteger exceptCount = new AtomicInteger(0);
        List<AppFinishDeviceBean> devices = pointBean.getDevices();
        if (CollectionUtil.isNotEmpty(devices)) {
            for (AppFinishDeviceBean device : devices) {
                // 设备停用自动处理,若提交时设备已启用, 则提示用户需重新检查并提交
                if (device.getAutoProcess() != null && device.getAutoProcess() == 1) {
                    BpDeviceFacilityCollection facilityCollection = bpDeviceFacilityCollectionService.findById(device.getDeviceId());
                    if (facilityCollection != null) {
                        // 获取设备装置状态
                        BpProductionFacility facility = bpProductionFacilityService.findOne(facilityCollection.getDeviceId());
                        if (facility != null && facility.getState() != 1) {
                            throw new ApiBusinessException(Status.DEVICE_WORKED_NEED_CHECK, device.getDeviceName());
                        }
                    }
                }
                List<AppFinishItemBean> checkItems = device.getCheckItems();
                if (CollectionUtil.isNotEmpty(checkItems)) {
                    // 检查项目
                    checkItems.forEach(checkItem -> {
                        // 生成recordItem
                        PatrolRecordItem patrolRecordItem = genCheckItem(patrolRecord, device, checkItem);
                        if (CheckResultStatus.ABNORMAL.equals(patrolRecordItem.getCheckResultState())) {
                            exceptCount.getAndIncrement();
                        }
                        recordItemService.saveOrUpdate(patrolRecordItem);
                    });
                }
            }
        }

        // 处理record，item有异常，record就算异常,并记录异常个数
        if (exceptCount.get() > 0) {
            patrolRecord.setCheckResult(CheckResultStatus.ABNORMAL);
            patrolRecord.setExceptCount(exceptCount.get());
            recordService.saveOrUpdate(patrolRecord);
        }
        // 处理任务
        dealTask(task, pointBean, recordCountBefore);

        return patrolRecord.getId();
    }

    public String getLocation() {
        Optional<BaseUser> op = baseUserDao.findById(Context.getCurrentUserId());
        if (op.isPresent()) {
            BaseUser baseUser = op.get();
            try {
                if (StringUtils.isNotEmpty(baseUser.getSn())) {
                    String sn = baseUser.getSn();
                    TrajectoryLastLocationResp lastLocation = personTrajectoryService.getLastLocation(sn, "", IdUtil.simpleUUID().toString());
                    if (lastLocation != null) {
                        JSONObject lastLocationBean = new JSONObject();

                        //如果最近一次点位在配置的时间内(10分钟) 则用最近一个点位的时间往前推60秒，看这段时间的点位是否在区域内
                        if (System.currentTimeMillis() - lastLocation.getTimestampMillisecond() <= getMaxTimeRange()) {
                            lastLocationBean.put("x", lastLocation.getLongitude());
                            lastLocationBean.put("y", lastLocation.getLatitude());
                            lastLocationBean.put("floorNo", lastLocation.getFloorNo());
                            lastLocationBean.put("time", lastLocation.getTimestampMillisecond());
                            return lastLocationBean.toString();
                        }
                    }
                }
            } catch (Exception e) {
                log.error("query last location error", e);
            }
        }
        return null;
    }

    private long getMaxTimeRange() {
        //最近一次点位时间配置
        //默认10分钟
        long maxTime = 10 * 60 * 1000;
        BaseConfig timeConfig = baseConfigService.findFirstConfig(BaseConfigCodes.PERSON_TRAJECTORY_TRACING_TIME_MAX);
        if (timeConfig != null && !StringUtils.isEmpty(timeConfig.getValue())) {
            maxTime = Long.parseLong(timeConfig.getValue().trim()) * 1000;
        }
        return maxTime;
    }

    private boolean hasBeenSubmit(AppFinishPointBean pointBean) {
        LambdaQueryWrapper<PatrolRecord> recordWrapper = new LambdaQueryWrapper<PatrolRecord>().last("limit 1");
        recordWrapper.eq(PatrolRecord::getTaskId, pointBean.getTaskId());
        recordWrapper.eq(PatrolRecord::getPointId, pointBean.getPointId());
        PatrolRecord record = recordService.getOne(recordWrapper);
        return record != null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int webDoTask(List<AppFinishPointBean> pointBeans) throws ApiBusinessException {
        int saveCount = 0;
        for (AppFinishPointBean pointBean : pointBeans) {
            pointBean.setReportWay("W");
            appDoTask(pointBean);
            saveCount++;
        }
        return saveCount;
    }

    PatrolRecordItem genCheckItem(PatrolRecord patrolRecord, AppFinishDeviceBean device, AppFinishItemBean checkItem) {
        PatrolRecordItem patrolRecordItem = new PatrolRecordItem();
        PatrolTask task = this.getById(patrolRecord.getTaskId());
        patrolRecordItem.setRecordId(patrolRecord.getId())
                .setCheckDeviceId(device.getDeviceId())
                .setCheckDeviceName(device.getDeviceName())
                .setCheckDeviceCode(device.getDeviceCode())
                .setId(checkItem.getRecodeItemId())
                .setCheckProjectId(checkItem.getItemId())
                .setCheckProjectName(checkItem.getCheckItem())
                .setCheckStandard(checkItem.getCheckStandard())
                .setResultType(checkItem.getResultType())
                .setSubResultType(checkItem.getSubResultType())
                // 三个检查结果和单位
                .setCheckResult(checkItem.getCheckResult())
                .setCheckResultUnit(checkItem.getCheckResultUnit())
                .setCheckResult1(checkItem.getCheckResult1())
                .setCheckResultUnit1(checkItem.getCheckResultUnit1())
                .setCheckResult2(checkItem.getCheckResult2())
                .setCheckResultUnit2(checkItem.getCheckResultUnit2())

                .setSymbol(checkItem.getSymbol())
                .setNormalLeft(checkItem.getNormalLeft())
                .setNormalRight(checkItem.getNormalRight())
                .setCheckResultState(checkItem.getCheckResultState())
                .setRemark(checkItem.getRemark());
        // 异常处理状态
        if (checkItem.getRecodeItemId() == null) {
            ArrayList<String> exceptInfoList = new ArrayList<>();
            Date currDate = new Date();
            if (CheckResultStatus.ABNORMAL.equals(checkItem.getCheckResultState())) {
                exceptInfoList.add("发现异常");
                // 发现异常，需处理
                patrolRecordItem.setExceptHandel(ExceptProcessStatus.NOT_PROCESS);
            }
            if (CheckResultStatus.ABNORMAL.equals(patrolRecord.getVerifyState())) {
                exceptInfoList.add("签到异常");
            }
            if (currDate.after(task.getEndTime())) {
                exceptInfoList.add("补检");
            }
            String exceptInfoStr = String.join(Constant.SPLIT_COMMA, exceptInfoList);
            patrolRecordItem.setExcepInfo(exceptInfoStr);
        }
        // 照片、视频、录音
        if (CollectionUtil.isNotEmpty(checkItem.getPicIds())) {
            String picIdStr = checkItem.getPicIds().stream().map(Object::toString).collect(Collectors.joining(Constant.SPLIT_COMMA));
            patrolRecordItem.setPicJson(picIdStr);
        }
        if (CollectionUtil.isNotEmpty(checkItem.getVideoIds())) {
            String videoIdStr = checkItem.getVideoIds().stream().map(Object::toString).collect(Collectors.joining(Constant.SPLIT_COMMA));
            patrolRecordItem.setVideoJson(videoIdStr);
        }
        if (CollectionUtil.isNotEmpty(checkItem.getVoiceIds())) {
            String voiceIdStr = checkItem.getVoiceIds().stream().map(Object::toString).collect(Collectors.joining(Constant.SPLIT_COMMA));
            patrolRecordItem.setVoiceJson(voiceIdStr);
        }
        return patrolRecordItem;
    }

    void dealTask(PatrolTask task, AppFinishPointBean pointBean, Integer recordCountBefore) {
        Long taskId = task.getId();
        // 检查任务下的未完成检查点数
        Integer countUnCheckPointByTask = taskMapper.countUnCheckPointByTask(taskId);
        // 更新任务状态
        if (recordCountBefore == 0) {
            // 此次提交是该task下第一次提交（第一个检查点）
            task.setStatus(TaskStatus.IN_PROGRESS);
        }
        // 开始时间取早
        if (task.getCheckStartTime() == null || task.getCheckStartTime().after(pointBean.getStartTime())) {
            task.setCheckStartTime(pointBean.getStartTime());
        }
        // 结束时间取晚
        if (task.getCheckEndTime() == null || task.getCheckEndTime().before(pointBean.getEndTime())) {
            task.setCheckEndTime(pointBean.getEndTime());
            task.setCheckEndTime(pointBean.getEndTime());
            task.setTimeCost(task.getCheckEndTime().getTime() - task.getCheckStartTime().getTime());
        }

        task.setCheckUser(pointBean.getCheckUserId() == null ? Context.getCurrentUserId() : pointBean.getCheckUserId());
        log.info("app do task taskId {} pointBean {}", taskId, pointBean);
        //该任务下不存在未检的检查点时，该任务完成
        if (countUnCheckPointByTask < 1) {
            task.setFinishDate(new Date());
            task.setStatus(TaskStatus.FINISHED);
            snapshotPathService.saveSnapshotPath(task);
            // 删除未完成task的映射，减轻定时任务的负担
            unfinishTaskService.deleteUnFinishTaskMapping(taskId);
        } else {
            log.info("app do task taskId {} pointBean {} countUnCheckPointByTask > 1", taskId, pointBean);
        }
        // task下的item存在的异常,决定task的 检查结果（和异常原因）
        // 如果检查点下无设备，且检查点签到异常时，无item，有record，则item中查不到签到异常，需手动补上
        Set<String> excSet = getExcepInfos(taskId);
        if (!excSet.contains("签到异常") && CheckResultStatus.ABNORMAL.equals(pointBean.getVerifyState())) {
            excSet.add("签到异常");
        }
        calculateAndSetTaskExcept(task, excSet);

        // task下存在的未整改检查项目数量
        Integer unRepairItem = taskMapper.countUnRepairItem(taskId);
        if (unRepairItem > 0) {
            task.setExcepHandel(ExceptProcessStatus.WAITING_TO_PROCESS);
        }
        saveOrUpdate(task);
    }

    void dealTaskWithNullPoint(AppFinishPointBean pointBean) {
        if (pointBean.getPointId() == null) {
            PatrolTask task = getById(pointBean.getTaskId());
            task.setStatus(TaskStatus.FINISHED);
            task.setCheckResult(CheckResultStatus.NORMAL);
            task.setCheckEndTime(pointBean.getEndTime() == null ? new Date() : pointBean.getEndTime());
            task.setStatus(TaskStatus.FINISHED);
            task.setFinishDate(new Date());
            if (task.getCheckStartTime() != null) {
                task.setTimeCost(task.getCheckEndTime().getTime() - task.getStartTime().getTime());
            } else if (task.getTimeCost() == null) {
                task.setTimeCost(0L);
            }
            task.setCheckUser(pointBean.getCheckUserId() == null ? Context.getCurrentUserId() : pointBean.getCheckUserId());
            saveOrUpdate(task);
            log.info("app do task with null point start save snap path task {}", task);
            // 不存快照
            snapshotPathService.saveSnapshotPath(task);
            // 删除未完成task的映射，减轻定时任务的负担
            unfinishTaskService.deleteUnFinishTaskMapping(pointBean.getTaskId());
        }
    }

    /**
     * 获取task下所有item的异常信息
     *
     * @param taskId
     * @return
     */
    Set<String> getExcepInfos(Long taskId) {
        List<String> excInfosList = taskMapper.selectExceptInfos(taskId);
        HashSet<String> result = new HashSet<>();
        if (CollectionUtil.isNotEmpty(excInfosList)) {
            excInfosList.forEach(excInfos -> {
                List<String> excInfo = Splitter.on(Constant.SPLIT_COMMA).trimResults().splitToList(excInfos);
                result.addAll(excInfo);
            });
        }
        return result;
    }

    void calculateAndSetTaskExcept(PatrolTask task, Set<String> itemExcSet) {
        // 无任何异常
        if (CollectionUtil.isEmpty(itemExcSet)) {
            task.setCheckResult(CheckResultStatus.NORMAL);
            return;
        }

        task.setCheckResult(CheckResultStatus.ABNORMAL);

        List<String> taskExcList = new ArrayList<>();

        if (itemExcSet.contains("发现异常")) {
            taskExcList.add("数据异常");
        }
        if (itemExcSet.contains("签到异常")) {
            taskExcList.add("未到位");
        }
        if (itemExcSet.contains("补检")) {
            taskExcList.add("补检");
        }
        if (itemExcSet.contains("未检")) {
            taskExcList.add("漏检");
        }
        String excReason = taskExcList.stream().collect(Collectors.joining(Constant.SPLIT_COMMA));
        task.setCheckResult("异常");
        task.setExceptReason(excReason);

    }

    @Override
    public String getTaskStatus(Long taskId) {
        PatrolTask task = getById(taskId);
        if (task != null) {
            return task.getStatus();
        }
        return null;
    }

    @Override
    public Long findLastTaskId(Long planId) {
        return taskMapper.findLastTaskId(planId);
    }

    @Override
    public Page<AppSynthesizeBean> synthesizeTask(Long pageNo, Long pageSize, Integer status, Date date,Integer category) {
        Long userId = Context.getCurrentUserId();
//        DateTime endTime = DateUtil.endOfDay(date);
//        DateTime startTime = DateUtil.beginOfDay(date);
        Map<String, Object> map = getTime(date, status);
        Page<AppSynthesizeBean> page = new Page<>(pageNo + 1, pageSize);
        Boolean way = getWay(date);
        Page<AppSynthesizeBean> beanPage = taskMapper.synthesizeTask(page, userId,(String) map.get("patrolStatus"),
                (String) map.get("investigateStatus"), (Date)map.get("startTime"), (Date)map.get("current"),(Date)map.get("endTime"),(Date)map.get("morrow"),
                category,way,status, (Date) map.get("nextDateStartTime"));
        if (beanPage.getRecords()!=null&& CollectionUtils.isNotEmpty(beanPage.getRecords())) {
            List<Long> pointIds = beanPage.getRecords().stream().filter(e -> e.getCategory().equals("隐患排查")).map(e -> e.getRiskPointId()).collect(Collectors.toList());
            List<Long> planIds = beanPage.getRecords().stream().filter(e -> e.getCategory().equals("巡检")).map(e -> e.getPlanId()).collect(Collectors.toList());
            Map<Long, ResultDto> planMap=new HashMap<>();
            if (CollectionUtils.isNotEmpty(planIds)) {
                List<ResultDto> beans = patrolPlanPointMappingService.getNumByPlanIds(planIds);
                planMap= beans.stream().collect(Collectors.toMap(ResultDto::getId, e -> e));
            }
            Map<Long, RiskDistrict> districtMap=new HashMap<>();
            if (CollectionUtils.isNotEmpty(pointIds)) {
                List<RiskPoint> list = riskPointService.findByIds(pointIds);
                districtMap = list.stream().collect(Collectors.toMap(RiskPoint::getId, r -> r.getRiskDistrict()));
            }
            for (AppSynthesizeBean record : beanPage.getRecords()) {
                if (record.getCategory().equals("隐患排查")){
                    getInvestigateBean(record,districtMap);
                }else {
                    getPatrolBean(record,planMap);
                }
                getTaskTimeAndStatus(record);
                record.setSurplusTime(getFormatCostTime(record.getTaskDeadLine().getTime()-System.currentTimeMillis()));
            }
        }
        return beanPage;
    }

    /**
     * 处理巡检逻辑
     * @return
     */
    public void getPatrolBean(AppSynthesizeBean record,Map<Long, ResultDto> planMap){
        if (planMap.get(record.getPlanId())!=null&&planMap.get(record.getPlanId()).getNum()!=null) {
            record.setCheckPointSize(planMap.get(record.getPlanId()).getNum().longValue());
        } else {
            record.setCheckPointSize(0L);
        }

//        PatrolCheckPlan plan = planMap.get(record.getPlanId());
//        if (plan != null) {
            CycleType cycleType = CycleType.find(record.getCycleType());
            if (cycleType != null) {
                String endDate = DateTimeUtil.formatDate(record.getTaskDeadLine(), "yyyy/MM/dd HH:mm");
                String startDate = DateTimeUtil.formatDate(record.getActiveTime(), "yyyy/MM/dd HH:mm");
                if (CycleType.CYCLE.equals(cycleType)) {
                    record.setCycleTypeDesc(record.getCycleDesc());

                    if (record.getTaskDeadLine().getTime() - record.getActiveTime().getTime() >= 24L * 3600 * 1000) {
                        String endDateStr = endDate.substring(0, 10);
                        String startDateStr = startDate.substring(0, 10);
                        record.setTempTimePeriod(startDateStr + " - " + endDateStr);
                    } else {
                        String endDateStr = endDate.substring(11, 16);
                        String startDateStr = startDate.substring(11, 16);
                        record.setTempTimePeriod(startDateStr + " - " + endDateStr);
                    }
                } else if (CycleType.SPECIFY.equals(cycleType)) {
                    record.setCycleTypeDesc(record.getCycleDesc());
                    record.setTempTimePeriod(startDate + " - " + endDate);
                } else if (CycleType.SINGLE.equals(cycleType)) {
                    record.setCycleTypeDesc(cycleType.getDesc());
                    record.setTempTimePeriod(startDate + " - " + endDate);
                }
//            }
        }
    }

    /**
     * @param date
     * @param status 1未开始 2已开始 3已结束
     * @return
     */
    public Map<String, Object> getTime(Date date, Integer status) {
        Map<String, Object> map = new HashMap<>();
        // 当前时间
        Date current = null;
        // 查询日起始时间
        Date startTime = DateUtil.beginOfDay(date);
        // 查询日次日起始时间
        Date nextDateStartTime = DateUtils.getDay(date,1);
        Date endTime = DateUtil.endOfDay(date);

        boolean now = isNow(date);

        String patrolStatus = null;
        String investigateStatus = null;
        if (status != null) {
            switch (status) {
                case 1:
                    if (now){
                        current = new Date();
                        startTime = new Date();
                        endTime = DateUtils.getDay(1);
                        patrolStatus = "待完成";
                        investigateStatus = "待完成";
                    } else {
                        current = DateUtils.getDate(date, 0);
                        patrolStatus = "未开始";
                        investigateStatus = "未开始";
                    }
                    break;
                case 2:
                    if (now){
                        current = new Date();
                        startTime = DateUtils.getDayNowHour(null);
                        endTime = new Date();
                    } else {
                        current = DateUtils.getDate(date, 0);
                        startTime = DateUtils.getDate(date, 0);
                        endTime = DateUtils.getDate(date, 1);
                    }
                    patrolStatus = "待完成";
                    investigateStatus = "待完成";
                    break;
                case 3:
                    if (now) {
                        startTime = DateUtils.getDate(date, 0);
                        nextDateStartTime = DateUtils.getDate(date, 1);
                    } else {
                        startTime = DateUtils.getDate(date, 0);
                        nextDateStartTime = DateUtils.getDate(date, 1);
                    }
                    patrolStatus="已完成";
                    investigateStatus="已完成";
                    break;
            }
        } else {
            if (now) {
                startTime = DateUtils.getDate(date, 0);
                nextDateStartTime =DateUtil.endOfDay(date);
                endTime = new Date();
                current = new Date();
            } else {
                startTime = DateUtils.getDate(date, 0);
                nextDateStartTime =DateUtil.endOfDay(date);
                current = DateUtils.getDate(date, 0);
                endTime = DateUtils.getDate(date, 1);
            }
        }
        map.put("nextDateStartTime", nextDateStartTime);
        map.put("startTime", startTime);
        map.put("endTime", endTime);
        if (patrolStatus!=null){
            map.put("patrolStatus", patrolStatus);
        }
        if (investigateStatus != null) {
            map.put("investigateStatus", investigateStatus);
        }
        if (current != null) {
            map.put("current", current);
        }
        return map;
    }
    /**
     * 判断时间是不是今天
     * @param date
     * @return    是返回true，不是返回false
     */
    private static boolean isNow(Date date) {
        //当前时间
        Date now = new Date();
        SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd");
        //获取今天的日期
        String nowDay = sf.format(now);
        //对比的时间
        String day = sf.format(date);
        return day.equals(nowDay);
    }


    /**
     * 处理隐患排查逻辑
     * @return
     */
    public void getInvestigateBean(AppSynthesizeBean record, Map<Long, RiskDistrict> map){
        if (map.get(record.getRiskPointId())!=null) {
            record.setDistrictId(map.get(record.getRiskPointId()).getId());
            record.setDistrictName(map.get(record.getRiskPointId()).getName());
        }
    }

    /**
     * 是否查询历史表数据
     * @param date
     * @return
     */
    public Boolean getWay(Date date){
        Boolean flag=true;
        Integer day=1;
        BaseConfig config = baseConfigService.findFirstConfig(BaseConfigCodes.INVESTIGATE_DATA_KEEP);
        if (config!=null) {
            if (StringUtils.isNotBlank(config.getValue())) {
                day= Integer.parseInt(config.getValue());
            }
        }
        Date day1 = DateUtils.getDay(-day);
        if (day1.before(date)) {
            flag=false;
        }
        return flag;
    }

    /**
     * 获取时分秒
     * @param time
     * @return
     */
    public String getSurplusTime(Long time){
        String timeStr="";
        SimpleDateFormat dateFormat = SimpleDateFormatCache.getHms();
        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT+00:00"));
        if (time>0){
            timeStr=dateFormat.format(time);
        }
       return timeStr;
    }

    /**
     * 剩余时间（毫秒数）转化为xx天xx小时xx分 格式
     */
    public static String getFormatCostTime(Long millisecond){
        StringBuilder costTime = new StringBuilder();
        long diffDays = millisecond / (24 * 60 * 60 * 1000);
        millisecond=millisecond-diffDays*(24 * 60 * 60 * 1000);
        long minute = millisecond / (1000 * 60*60);
        millisecond=millisecond-minute*(1000 * 60*60);
        long second = millisecond / (1000 * 60);
        // 大于24小时就只显示天数，不显示时、分
        if (diffDays>0){
            costTime.append(diffDays).append("天");
        }
        if (minute > 0 && diffDays == 0){
            costTime.append(minute).append("小时");
        }
        if (second > 0 && diffDays == 0){
            costTime.append(second).append("分");
        }
        return costTime.toString();
    }


    /**
     * 处理任务开始和结束时间 状态
     * @param bean
     */
    public void getTaskTimeAndStatus(AppSynthesizeBean bean) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
        if (bean.getCategory().equals("隐患排查")) {
            if (bean.getShowDesc().equals("周期性") && (bean.getCheckRateDesc().contains("时"))) {
                bean.setTaskStartTime(dateFormat.format(bean.getActiveTime()));
                bean.setTaskEndTime(dateFormat.format(bean.getTaskDeadLine()));
            }
            // 周期性天任务, 起始时间和结束时间都是同一天00:00   产品要求在结束时间+1天展示
            // eg: 数据库 start_time：2023-03-30 00:00:00 end_time: 2023-03-30 00:00:00
            // 返回给前端  start_time：2023-03-30 00:00:00 end_time: 2023-03-31 00:00:00
            if (bean.getShowDesc().equals("周期性")&&bean.getSourceType()==0
                    &&bean.getPeriodDays()==null&&bean.getPeriodUnit().contains("日")
                    &&bean.getCheckRateDesc().contains("每")
                    &&bean.getTaskStartTime().compareTo(bean.getTaskEndTime()) == 0) {
                Calendar calendar = new GregorianCalendar();
                calendar.setTime(bean.getTaskDeadLine());
                calendar.add(Calendar.DATE, 1);
                bean.setTaskEndTime(DateUtil.formatDateTime(calendar.getTime()));
                bean.setTaskStartTime(DateUtil.formatDateTime(bean.getActiveTime()));
                bean.setTaskDeadLine(calendar.getTime());
            }
            if (bean.getStatusName().equals("已完成")) {
                if (StringUtils.isBlank(bean.getCheckResult()) || bean.getCheckResult().equals("未检")) {
                    bean.setStatusName("逾期未完成");
                } else {
                    bean.setStatusName("已完成-" + bean.getCheckResult());
                }
            }  else if (bean.getStatusName().equals("待完成")||bean.getStatusName().equals("未完成")) {
                if (bean.getActiveTime().before(new Date())) {
                    if (bean.getTaskDeadLine().before(new Date())) {
                        bean.setStatusName("逾期未完成");
                    } else {
                        bean.setStatusName("待执行");
                    }
                }else{
                    bean.setStatusName("未开始");
                }
            } else if (bean.getStatusName().equals("被完成")) {
                bean.setStatusName("已完成-未参与");
            }
        } else {
            if (bean.getCheckRateDesc().contains("小时")&&bean.getCheckRateDesc().contains("次")) {
                bean.setTaskStartTime(dateFormat.format(bean.getActiveTime()));
                bean.setTaskEndTime(dateFormat.format(bean.getTaskDeadLine()));
            }else {
                bean.setTaskStartTime(DateUtil.formatDateTime(bean.getActiveTime()));
                bean.setTaskEndTime(DateUtil.formatDateTime(bean.getTaskDeadLine()));
            }

            if (bean.getStatusName().equals("已完成")) {
              if (bean.getCheckResult()!=null&&bean.getCheckResult().contains("漏检")) {
                    bean.setStatusName("逾期未完成");
                } else {
                  if (bean.getCheckResult()!=null&&!bean.getCheckResult().contains("漏检")){
                      if (bean.getCheckResult().contains("异常")) {
                          bean.setStatusName("已完成-异常");
                      } else {
                          bean.setStatusName("已完成-正常");
                      }
                  }else {
                      if (bean.getTaskDeadLine().before(new Date())){
                          bean.setStatusName("逾期未完成");
                      }else {
                          String statusName = bean.getCheckResult();
                          if (StringUtils.isBlank(bean.getCheckResult())) {
                              statusName = "未参与";
                          }
                          bean.setStatusName("已完成-" + statusName);
                      }
                  }
              }
            }
        }
        if (bean.getStatusName().equals("未开始")) {
            bean.setStatusName("未开始");
        } else if (bean.getStatusName().equals("待完成")) {
            if (bean.getActiveTime().before(new Date())) {
                if (bean.getTaskDeadLine().before(new Date())) {
                    bean.setStatusName("逾期未完成");
                } else {
                    bean.setStatusName("待执行");
                }
            } else {
                bean.setStatusName("未开始");
            }
        }

    }



    /**
     * 位置校验
     *
     * @param pointId
     * @param userId
     * @return
     * @throws ApiCoreException
     */
    public PositionCheckBean position(Long pointId, Long userId) throws ApiCoreException {
        // 检查点位置处理
        PatrolCheckPoint checkPoint = pointService.getById(pointId);
        String pointLocationStr = checkPoint.getLocation();
        if (StringUtils.isBlank(pointLocationStr)) {
            throw new IllegalArgumentException("该检查点没有位置信息");
        }

        // 检查点位置
        LocationBean pointLocation = JsonUtil.fromJson(pointLocationStr, LocationBean.class);
        // 位置点位数组
        List<LocationPointsBean> points = pointLocation.getPoints();
        Double[] pointLons = new Double[points.size()];
        Double[] pointLats = new Double[points.size()];
        getCoordinateArr(points, pointLons, pointLats);

        // 人员位置处理
        BaseUser user = userDao.findById(userId).get();
        String sn = user.getSn();
        // 定位卡号不为空
        if (org.springframework.util.StringUtils.isEmpty(sn)) {
            throw new IllegalArgumentException(Status.USER_SN_IS_NULL.getMsg());
        }
        TrajectoryLastLocationResp userLocation = personTrajectory.getLastLocation(sn, null, IdUtil.simpleUUID());
        // 未获取到人员位置或最后一个点位在十分钟之外
        if (userLocation == null || (new Date().getTime() - userLocation.getTimestampMillisecond()) > 1000 * 60 *10) {
            log.error("人员位置获取失败：{}", userLocation);
            return new PositionCheckBean(false, null);
        }
        String userLocationStr = JsonUtil.toJson(userLocation);
        // 用户位置处理
        Double userLon = userLocation.getLongitude();
        Double userLat = userLocation.getLatitude();
        String floorNo = userLocation.getFloorNo();
        // 开放平台楼层转换
        Long floorId = Long.parseLong(floorNo.replace(Constant.FLOOR_REPLACE_STR, Constant.EMPTY_STR));
        if (!floorId.equals(pointLocation.getFloorId())) {
            log.error("位置校验失败-楼层异常");
            return new PositionCheckBean(false, null);
        }
        boolean inPolygon = RegionUtil.isInPolygon(userLon, userLat, pointLons, pointLats);
        return new PositionCheckBean(inPolygon, userLocationStr);
    }

    @Override
    public void exportTaskRecord(TaskRecordSearchBean searchBean, HttpServletRequest request, HttpServletResponse response) throws IOException {
        searchBean.setPage(0);
        searchBean.setSize(20000);
        Page<WebTaskDto> resultPage = getTaskHadDone(searchBean);
        List<WebTaskExportBean> taskList = convertExportTaskList(resultPage.getRecords());
        OutputStream out = response.getOutputStream();
        ExcelWriter writer = ExcelUtil.getWriter(true);
        buildExcelHeader(writer);
        writer.write(taskList, true);
        String fileName = FileUtil.getEncodeFileName(request, "检查记录");
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition", String.format("attachment;filename=%s", fileName + ".xlsx"));
        writer.flush(out, true);
        writer.close();
        IoUtil.close(out);
    }

    private List<WebTaskExportBean> convertExportTaskList(List<WebTaskDto> records) {
        List<WebTaskExportBean> taskList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(records)) {
                records.forEach(each -> {
                    WebTaskExportBean taskExportBean = new WebTaskExportBean();
                    BeanUtils.copyProperties(each, taskExportBean);
                    taskExportBean.setActualFinishTime(DateUtil.formatDateTime(each.getActualFinishTime()));
                    taskExportBean.setTaskStartTime(DateUtil.formatDateTime(each.getTaskStartTime()));
                    taskList.add(taskExportBean);
                });
        }
        return taskList;
    }


    private void getCoordinateArr(List<LocationPointsBean> points, Double[] lonArr, Double[] latArr) {
        int i = 0;
        for (LocationPointsBean each : points) {
            lonArr[i] = each.getX();
            latArr[i] = each.getY();
            i++;
        }
    }

    private void buildExcelHeader(ExcelWriter writer) {
        writer.addHeaderAlias("taskId", "检查记录编号");
        writer.addHeaderAlias("planName", "检查计划名称");
        writer.addHeaderAlias("respDepartName", "责任部门");
        writer.addHeaderAlias("actualFinishTime", "检查完成时间");
        writer.addHeaderAlias("taskStartTime", "任务时间");
        writer.addHeaderAlias("checkUserName", "巡检人员");
        writer.addHeaderAlias("timeCostStr", "巡检用时");
        writer.addHeaderAlias("checkResultInfo", "检查结果");
        writer.addHeaderAlias("excepHandel", "异常处理");

    }

}
